package com.jlect.swebing.ui.laf.basic.client;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventPreview;
import com.jlect.swebing.renderers.client.RendererEventConstants;
import com.jlect.swebing.ui.client.*;
import com.jlect.swebing.ui.client.laf.EventListener;
import com.jlect.swebing.ui.client.laf.EventProvider;
import com.jlect.swebing.util.client.Logger;

/**
 * This class sources events that happend in web application.
 *
 * @author Sergey Kozmin
 * @since 29.11.2007 9:29:54
 */
public class EventProviderGWTImpl implements EventProvider, EventPreview {
    private static Logger logger = Logger.getLogger("GWTEventProvider");

    public static final int ALL_MOUSE_EVENTS_MASK = Event.MOUSEEVENTS | Event.ONCLICK | Event.ONDBLCLICK;

    private EventListener listener;

    public EventProviderGWTImpl() {
        DOM.addEventPreview(this);
    }

    public EventListener setEventListener(EventListener listener) {
        EventListener prevListener = this.listener;
        this.listener = listener;
        return prevListener;
    }

    public boolean onEventPreview(Event event) {
        if(listener == null) {
            return true;
        } else {
            GEvent evt = createEvent(event);
            if(evt != null) {
                return listener.eventAppeared(createEvent(event));
            } else {
                return true;//cancel focus events todo (check if we need to propagate other type of events)
            }
        }
    }

    private GEvent createEvent(Event event) {
        GEvent ret = null;
        if ((DOM.eventGetType(event) & Event.KEYEVENTS) != 0) {
            ret = new KeyEventImpl(event, getTargetUI(event), DOM.eventGetKeyCode(event),
                    getKeyType(DOM.eventGetType(event)), getModifier(event));
        } else if((DOM.eventGetType(event) & ALL_MOUSE_EVENTS_MASK) != 0) {
            ret = new MouseEvent(event, getTargetUI(event), getMouseType(DOM.eventGetType(event)), getModifier(event));
        } else {
            logger.info("Event wasn't handled, type [" + DOM.eventGetType(event) + "]. ");
        }
        return ret;
    }

    private int getModifier(Event event) {
        int modifiers = 0;
        if(DOM.eventGetShiftKey(event)) {
            modifiers |= RendererEventConstants.SHIFT_MASK;
        }
        if(DOM.eventGetCtrlKey(event)) {
            modifiers |= RendererEventConstants.CTRL_MASK;
        }
        if(DOM.eventGetAltKey(event)) {
            modifiers |= RendererEventConstants.ALT_MASK;
        }
        if(DOM.eventGetMetaKey(event)) {
            modifiers |= RendererEventConstants.META_MASK;
        }
        return modifiers;
    }

    private int getKeyType(int gwtType) {
        int ret;
        switch (gwtType) {
            case Event.ONKEYDOWN : {
                ret = KeyEvent.KEY_TYPED;
                break;
            }
            case Event.ONKEYPRESS : {
                ret = KeyEvent.KEY_PRESSED;
                break;
            }
            case Event.ONKEYUP: {
                ret = KeyEvent.KEY_RELEASED;
                break;
            }
            default : throw new IllegalArgumentException("Could not convert renderer event [" +
                    gwtType + "] to a key event. ");
        }
        return ret;
    }

    private int getMouseType(int gwtType) {
        int ret;
        switch(gwtType) {
            case Event.ONCLICK      : ret=MouseEvent.CLICK_EVENT;   break;
            case Event.ONMOUSEDOWN  : ret=MouseEvent.MOUSE_DOWN ;   break;
            case Event.ONMOUSEMOVE  : ret=MouseEvent.MOUSE_MOVE ;  break;
            case Event.ONMOUSEOUT   : ret=MouseEvent.MOUSE_OUT  ;  break;
            case Event.ONMOUSEOVER  : ret=MouseEvent.MOUSE_OVER ;  break;
            case Event.ONMOUSEUP    : ret=MouseEvent.MOUSE_UP   ;  break;
            case Event.ONMOUSEWHEEL : ret=MouseEvent.MOUSE_WHEEL;  break;
            default                 : throw new IllegalArgumentException("Mouse event with code [" + gwtType + "] couldn't be found. ");
        }
        return ret;
    }

    private int convertEventType(int gwtEventType) {
        int ret;
        switch(gwtEventType) {
            case Event.ONBLUR       : ret=RendererEventConstants.ONBLUR ;        break;
            case Event.ONCHANGE     : ret=RendererEventConstants.ONCHANGE ;      break;
            case Event.ONCLICK      : ret=RendererEventConstants.ONCLICK ;       break;
            case Event.ONDBLCLICK   : ret=RendererEventConstants.ONDBLCLICK ;    break;
            case Event.ONERROR      : ret=RendererEventConstants.ONERROR ;       break;
            case Event.ONFOCUS      : ret=RendererEventConstants.ONFOCUS ;       break;
            case Event.ONKEYDOWN    : ret=RendererEventConstants.ONKEYDOWN ;     break;
            case Event.ONKEYPRESS   : ret=RendererEventConstants.ONKEYPRESS ;    break;
            case Event.ONKEYUP      : ret=RendererEventConstants.ONKEYUP ;       break;
            case Event.ONLOAD       : ret=RendererEventConstants.ONLOAD ;        break;
            case Event.ONLOSECAPTURE: ret=RendererEventConstants.ONLOSECAPTURE ; break;
            case Event.ONMOUSEDOWN  : ret=RendererEventConstants.ONMOUSEDOWN ;   break;
            case Event.ONMOUSEMOVE  : ret=RendererEventConstants.ONMOUSEMOVE  ;  break;
            case Event.ONMOUSEOUT   : ret=RendererEventConstants.ONMOUSEOUT   ;  break;
            case Event.ONMOUSEOVER  : ret=RendererEventConstants.ONMOUSEOVER  ;  break;
            case Event.ONMOUSEUP    : ret=RendererEventConstants.ONMOUSEUP    ;  break;
            case Event.ONMOUSEWHEEL : ret=RendererEventConstants.ONMOUSEWHEEL ;  break;
            case Event.ONSCROLL     : ret=RendererEventConstants.ONSCROLL     ;  break;
            default                 : ret=RendererEventConstants.UNDEFINED    ;  break;
        }
        return ret;
    }


    /**
     * Determines the component associated with the specified event.
     *
     * @param event the event to be handled
     * @return the {@link com.jlect.swebing.ui.client.GComponent} associated with the event,
     * or <code>null</code> if none is found.
     */
    private GComponent getTargetUI(Event event) {
        Element element = DOM.eventGetTarget(event);
        GComponent ret = null;
        for (; element != null; element = DOM.getParent(element)) {
            if((ret = ComponentsRegister.getRegister().getComponent(element)) != null) {
                break;
            }
        }
        return ret;
    }
}

